Skip to content

feat(service-automation): the connector connection store, the connect door and the OAuth callback read and write one instance by its package and name - #33218

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-y6vjs1
Nov 14, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-y6vjs1

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Tesla · project thread
Part of #31699

Clause-②: yes (widening)
Metadata-protocol: none

维护者速读

  • 改了什么:连接存储、连接门(读 / 开始 / 回调 / 撤销)、OAuth 回调和派发都按「包 + 名字」读写连接。开始连接时把实例的包记进待完成授权,回调只完成那个包的实例,不再按名字重新解析。没有包的旧连接行,只在名字的持久解析指向该包时才算它的,该包重新连接时就地认领。
  • 为什么改:ADR-0150 D4,objectql: narrow the phase-1 one-holder refusal from environment to namespace for webhook, connector and email_template (ADR-0150 child B, webhook family) #31699 PR 3a 的第二半(第一半 feat(service-automation, metadata-protocol, driver-mongodb, cli): a connector connection is unique per organization and instance (package, name) #33199 加宽了键)。
  • 不变:每个名字只有一个持有者、每个组织一条连接的库上,所有答复照旧;一持有者兜底仍在。
  • 新的拒绝(逐条列在 changeset 的 "What answers differently"):名字有两个持有者时连接门 409;同一组织有两条行回答同一实例时门 409、派发 503(不再挑一条);回调记录的包不再声明该实例时 404。
  • 声明的偏离:M5(本 PR 不加 ?package=,地址化的门随收窄兜底的 PR 一起落,不晚于它)、M6(无持有者时用条目自己的包)、S3(两个持有者时包自己的行优先)。三条都经红线审读接受。
  • 先看:packages/services/service-automation/src/connector-connections.ts(answering)、src/plugin.ts(connectorInstancePackage、connectionDoorInstance)、src/oauth2-grant/grant-handle.ts。

What changes

  • Store (ConnectorConnectionStore): connect, read, readOrRefuse, revoke, listFor take the instance's package. One rule (answering) decides which rows answer: the package's own rows, plus a package-less row only where the name's durable resolution (the boot backfill's question) binds it to that package or no registry can judge; a connect adopts that row in place (M2). Two answering rows are refused, never picked. A package-less row of a name two packages hold is no package's: the read and the revoke are refused 409 naming os migrate stored-references bind. listFor still counts a conflicted organization, carrying the refusal. The post-insert take-back from feat(service-automation, metadata-protocol, driver-mongodb, cli): a connector connection is unique per organization and instance (package, name) #33199 now counts by package.
  • Instance package (connectorInstancePackage): a real stamp; none for the environment's stored entry; for an unstamped or compatibility-default entry, the durable holder's package. Door, dispatch handle, materializer and reconnect mark all use it.
  • Door (connectionDoorInstance): resolves the name; two holders 409; no registry or no holder serves the one declared entry (two declared entries 409).
  • Pending authorization: carries packageId (explicit null for no package). The callback completes exactly that package's entry; a record from before this change (no key, TTL ten minutes) resolves the name.
  • Dispatch (createOAuth2GrantHandle): reads by package; a conflict is 503 on both the selection and the refresh read; an org-less dispatch never takes a conflicted organization's row.
  • Docs: one sentence in content/docs/automation/connectors.mdx.

Declared deviations from the design review

  • M5: no ?package= on the HTTP door and no spec change here. The addressed door lands with the PR that narrows the one-holder backstop, and no later. Until then a name with two holders (a package installed after boot) is refused at every leg of the door, revoke included.
  • M6: with no registry answer or no holder, the one declared entry is served with its own package, not null.
  • S3: under two holders a package's own row wins over a package-less row of the name; the stray row keeps its token until bound and revoked (remedy in the changeset).

Verification

  • connector-connection-package.test.ts (new, real ObjectQL over SQLite): 10 pins on the store rule, adoption, two holders, two rows, revoke, listFor.
  • connector-connection-door.test.ts: 13 new pins through the booted plugin and a real SchemaRegistry: stamping, the 409s, the callback completing the recorded package (including null) after the declarations change, refusal before the code is used, M2 adoption, two rows refused with and without the run's organization, a conflicted organization still counted (422), revoke 409 under a disabled second holder, unstamped and __runtime__ entries keeping their row across the backfill bind.
  • Ablations (each reds its pins): connect stamps no package; begin records no package; callback re-resolves; refused resolution not refused; two declared entries picked; adoption not stamping; org-less takes a conflicted entry; listFor skips a conflicted organization; revoke answers not-connected under conflict; door fallback removed; the resolver arm of the instance package removed.
  • @objectstack/service-automation full suite, rebased on main after feat(service-automation, metadata-protocol, driver-mongodb, cli): a connector connection is unique per organization and instance (package, name) #33199: 281 files, 3871 tests passed. tsc --noEmit, ESLint on touched files, tenant-audit-census, check-engine-double-contract green.

Red-line read

Run in this thread by a separate review pass (maintainer's instruction: 「你自己有fable,自己审就可以了吧?」). Round 1: PASS-WITH-CHANGES, one must-fix (an org-less dispatch could take the first of two answering rows) and five should-fix (a conflicted organization skipped turning a 422 into a dispatch; the door's holder-to-entry match; revoke 404 vs read 409; revoke under two holders; the changeset's "as before"), all taken; M5, M6 and S3 accepted. Round 2: PASS-WITH-CHANGES, one should-fix (an unstamped entry must be its durable holder's instance, or the backfill bind loses its row), taken. Round 3: PASS. Carried to 3b: re-key the grant handles by (package, name) and evaluate the handle's package per read, before any PR lets two instances of one name live.

These red-line reads ran before the 2026-11-14 04:37Z pause of seat-run contract reviews. This PR is opened as a draft and owes a contract review at TIER_DEFAULT (widening) from the new review agent before it is marked ready.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic.
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes.
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: its generated artifacts are regenerated and committed.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English.
  • The Before Submitting list in CONTRIBUTING.md is done.

🤖 Generated with Claude Code

https://claude.ai/code/session_01L9qibzKCovWpJoMZD6U3NT

… door and the OAuth callback read and write one instance by its package and name

The store reads, connects, revokes and lists by (organization, package,
name). A package-less row answers a package's instance only where the
name's durable resolution binds it there (or no registry can judge), and
that package's connect adopts it in place. Two answering rows are refused
409 at the door and 503 at a dispatch, never one used; an organization
whose read is refused is still counted. The door resolves the name to one
declared instance; the begin records its package in the pending
authorization and the callback completes exactly that package's entry.
An unstamped entry is the instance of the package its name's durable
resolution holds it under, for the door and the dispatch alike.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibzKCovWpJoMZD6U3NT
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 14, 2026
@github-actions

github-actions Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/service-automation, touching 50 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/services/service-automation/src/stored-reference-package-backfill.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

46 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json e540baf8873c22642ce191b99e7e5ce1fd3b4620.

⛔ 17 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/services/service-automation/src/stored-reference-package-backfill.ts) — pages documenting those are invisible to this run
  • 1 anchor(s) matched too much of the corpus to be a work list: organization_id (literal, 40 pages)
  • 7 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 65 of 247 client-bound route-ledger rows — the other 182 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 182: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 59 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 123 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 11 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json e540baf8873c22642ce191b99e7e5ce1fd3b4620 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 1e977dd8bf0d607fa0176fa1ca2583318ff945b4 — the merge of head 0704ffc0478e381ff0eaf59b5c75324bf8eb790c into base e540baf8873c22642ce191b99e7e5ce1fd3b4620, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 1e977dd8bf0d607fa0176fa1ca2583318ff945b4 && git checkout 1e977dd8bf0d607fa0176fa1ca2583318ff945b4
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e540baf8873c22642ce191b99e7e5ce1fd3b4620 0704ffc0478e381ff0eaf59b5c75324bf8eb790c && git checkout -B drift-repro e540baf8873c22642ce191b99e7e5ce1fd3b4620 && git merge --no-ff 0704ffc0478e381ff0eaf59b5c75324bf8eb790c

node scripts/docs-audit/affected-docs.mjs --json e540baf8873c22642ce191b99e7e5ce1fd3b4620

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs e540baf8873c22642ce191b99e7e5ce1fd3b4620 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#7 · os-tesla · session_01L9qibzKCovWpJoMZD6U3NT

Contract-review-claim: session_01L9qibzKCovWpJoMZD6U3NT · head e72a245 · 2026-11-14T14:19Z

The maintainer lifted the review pause at 14:17Z. An isolated reviewer sub-agent of the card thread is reviewing this head at TIER_DEFAULT (declaration face).

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#7 · os-tesla · session_01L9qibzKCovWpJoMZD6U3NT

Posted 2026-11-14T14:33Z. The brief the card thread handed its isolated reviewer sub-agent for the record below, verbatim.

Contract review brief — PR #33218 @ e72a24582d75ce784e71810e0ff8933c41f9d074 (card #31699)

You review at TIER_DEFAULT. Render ONE ## Contract review record on this head — its shape is
node scripts/pm/record-recognisers.mjs --template --face declaration — with a PASS or FAIL verdict, and post it as ONE comment
on the PR or its card.

Inputs — these and nothing else:
· card #31699: its body and EVERY comment (rulings and earlier records included)
· PR #33218: its body, its file list, and the net diff against main at e72a24582d75ce784e71810e0ff8933c41f9d074
· the check-runs on e72a24582d75ce784e71810e0ff8933c41f9d074: their conclusions ARE the gate verdicts
⛔ Not the dispatch order, ⛔ not the dispatching seat's own conclusions — this brief is adversarial by design.

⛔ READ-ONLY. No worktree, no checkout, no build, no test run, no gate re-run, no ablation: the check-runs
on the head already answered every derived gate family, and a local run costs more than it returns.
If ONE probe is unavoidable, the record's Local-runs: line says probe — REASON; otherwise none.
Scratch space is SCRATCHPAD/pr-33218/ alone; nothing there you did not write this round is read.

Judge, in the record's own sections:
① Derived judgments — every accept-set and public-surface change the diff implies, each named right or wrong.
② Semver level — the changeset (or skip-changeset) matches what the diff publishes, and the Clause-②: line.
③ Boundary flags — every dev flag and every open_questions entry answered, or escalated.
Sign Implemented-by: with the dev's branch and Reviewed-by: with your session id — the value is the FIRST
thing after each colon.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#7 · os-tesla · session_01L9qibzKCovWpJoMZD6U3NT

Posted 2026-11-14T14:34Z. An isolated reviewer sub-agent of the card thread produced this record from the brief alone (card #31699 with all 37 comments, PR #33218's body, file list and net diff, and the check-runs on the head).

Contract review

Served-tier: TIER_DEFAULT
Head-sha: e72a24582d75ce784e71810e0ff8933c41f9d074
Local-runs: none

① Derived judgments

Read: 11 files, +866 / -72, all under packages/services/service-automation/src/ plus one changeset and one sentence in content/docs/automation/connectors.mdx. No packages/spec, packages/runtime or packages/core file. Check-runs on the head: 53, of which 38 success, 15 skipped, 0 failed.

  • Store rule (answering, connector-connections.ts:225): right. A package reads its own rows, plus a package-less row only where the durable resolution names that package or no registry can judge. Two answering rows are refused, never picked. A package-less row of a two-holder name is no package's. This is ADR-0150 D4 and red lines 1 and 2.

  • Door 409 for a two-holder name, and for two declared entries with no registry (plugin.ts:4501): right. The answer it replaces was the first declared entry, which was a guess. Two holders cannot boot while the backstop stands, so no supported caller loses an answer.

  • Callback completes the recorded package; 404 when that package no longer declares the instance: right. The refusal comes before the code is exchanged. A record with no key resolves the name for its remaining TTL, so an in-flight connect survives the upgrade.

  • Dispatch 503 on a conflict, on both the selection and the refresh read (grant-handle.ts): right. An org-less dispatch counts a conflicted organization and never takes its row.

  • Legacy package-less row, one holder: right. It is read before the backfill binds it, read after, and stamped in place by a reconnect. Entries with no stamp or the __runtime__ stamp take the durable holder's package, so the bound row stays theirs. Both are pinned through the booted plugin.

  • Deviations M5, M6 and S3: right. None loosens resolution. M5 leaves a two-holder name refused at every leg. M6 stamps the entry's own package and the stamped row answers only that package. S3 serves a package its own row, which is not ambiguous.

  • F1 (should-fix): a one-holder answer moves, and the declaration says none does.

    • (a) Certain. .changeset/31699-connection-store-package.md:34 states it: an instance whose declaration moved to another package reads "not connected". After feat(service-automation, metadata-protocol, driver-mongodb, cli): a connector connection is unique per organization and instance (package, name) #33199 the boot backfill stamps every row, and this PR stamps every new connect. So when the declaring package's id changes (a connector moved between packages, moved to the environment, or a renamed host package), a name that has one holder before and after loses its connection. On main the read is by name and the connection keeps dispatching. connector-connections.ts:225-238 returns only own for the new package.
    • The same line says the old row is read by no door "until that package declares the name again or the row is bound". The bind refuses it: packages/cli/src/utils/stored-package-references.ts:643-646 answers already-bound, "a bound row is never moved". The door's revoke resolves to the new package and leaves the row. So an encrypted refresh token stays stored with no door or CLI path to remove it. On main a revoke by name removed it.
    • (b) To answer. plugin.ts:4519-4524 and the pin at connector-connection-door.test.ts:1090 serve an entry stamped with package P while the name resolves to package Q. In that composition a legacy package-less row is read by nobody: answering gives P only its own rows, and the backfill binds the row to Q. The PR does not say which real composition produces this, and no pin reads a legacy row there.
    • Why it is not a nit: ADR-0150 says adding the coordinate "changes no observable answer for a name phase 1 keeps to one holder"; the card's acceptance (b) allows only its declared narrowing and sends any other to the maintainer; epic ruling 6816940784 R2-C on this card held "a one-holder answer must not move. Declaring the move is not enough."
    • Fix, one of: keep the answer (for example the revoke leg also removes rows naming a package that no longer declares the name while one holder stands, and state what the new holder reads); or take a maintainer ruling that a moved declaration is a new instance under the 01:02Z key decision, declare it on the Clause-② line, and give a remedy that works for the stranded row. Either way correct line 34's "or the row is bound", and answer (b) with the composition named or a pin.
  • N1 (nit): .changeset/31699-connection-store-package.md:40 (S3) gives a remedy that cannot run in the state it describes. It says to revoke the package's own row first, but line 38 (M5) says revoke is refused 409 under two holders. The working path is to uninstall one holder; the one-holder read then refuses two rows and the revoke clears both.

  • N2 (nit): connector-connections.ts:642 is unreachable in effect. answering throws the conflict only when the package has no row of its own, so the filter on the next line is always empty and line 643 always rethrows.

  • N3 (nit): plugin.ts:4173 fixes the grant handle's package when the handle is created, while the door asks per request. The PR body carries the per-read evaluation to 3b. With one holder and a real stamp the two agree.

② Semver level

  • '@objectstack/service-automation': patch, with Metadata-protocol: none: right. No authorable key, value or type moves, and AGENTS.md Post-Task step 3 ships a non-protocol change as patch. Check Changeset is green. The ADR-0087 marker (not-required) fits: no authorable schema changes.
  • The declaration line, as written in the PR body and at changeset line 7, is Clause-②: yes (widening). yes is right. The arm and its sentence are wrong as written (F1): the diff publishes no wider accept set on any public surface (the changeset itself says "In-process APIs. None public"), every item under "What answers differently" is a tighter answer, and one of them is reachable with one holder, one row and the guard standing. The sentence "on a database where each organization keeps one connection of an instance and the name has one holder, every answer is as before" is contradicted by line 34.
  • If the moved-declaration answer stays as built, the line that matches the diff carries the (narrowing) arm. That arm is BREAKING and its review is owed at CONTRACT_REVIEW_TIER, which this record does not serve.

③ Boundary flags

  • No os-dev-report for this PR is on the card. The latest report (6869754560) is for feat(service-automation, metadata-protocol, driver-mongodb, cli): a connector connection is unique per organization and instance (package, name) #33199. The flags read here are the PR body's and handover 6871869590's.
  • M5, M6, S3: answered in ①, accepted.
  • "Carried to 3b: re-key the grant handles by (package, name) and evaluate the handle's package per read": answered as N3; correct to carry while one holder stands.
  • File surface against claim amendment 6869821095: stored-reference-package-backfill.ts is outside it. The change there is three comment lines (:48-50) that keep the R11 note true. Accepted as a deviation; the ACCEPT should name it.
  • open_questions: none stated for this PR. F1 (b) is this record's question to the dev.
  • Author: the PR's user.login is objectstack-fleet[bot]; no further note is owed.
  • Escalated: F1's ruling option belongs to the maintainer, per the card's acceptance line on a narrowing beyond the declared one.

Implemented-by: claude/project-thread-y6vjs1
Reviewed-by: session_01L9qibzKCovWpJoMZD6U3NT

VERDICT: FAIL

…connection row of the name answers its instance, so a declaration that moves keeps its connection

The contract review found that a declaration moving to another package (or
to the environment) read "not connected" under one holder, and its old row
could be neither read nor revoked. Under one holder, or with no registry to
judge, the store now answers every row of the name, as the read by name did;
a connect adopts and restamps the row, null included, and a revoke removes
it. Under two holders a package reads its own rows, and a row naming none of
the holders is no package's.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibzKCovWpJoMZD6U3NT
@github-actions github-actions Bot added size/xl and removed size/l labels Nov 14, 2026
…lared entry the door serves where the name resolves to another package

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibzKCovWpJoMZD6U3NT
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#7 · os-tesla · session_01L9qibzKCovWpJoMZD6U3NT

Posted 2026-11-14T14:37Z. This is the same isolated reviewer's delta review of the fix for FAIL record 6878911246, under brief 6878905826, read as e72a2458..0704ffc0 (two commits) plus the PR body and the check-runs on the new head.

Contract review

Served-tier: TIER_DEFAULT
Head-sha: 0704ffc0478e381ff0eaf59b5c75324bf8eb790c
Local-runs: none

① Derived judgments

Delta: 5 files, +148 / -67. connector-connections.ts (the rule), a three-line comment in plugin.ts, the changeset, and the two test files. No new file, no path outside the earlier surface.

The new rule (connector-connections.ts:231-254): right. While the name's durable resolution does not refuse (one holder, the environment's item, no holder, or no registry), every row of the name in the organization answers the instance, whatever package the row names. That is main's read by name, so no one-holder answer moves. Only under a refused resolution (two holders) does each package read its own rows; a row naming no package or a non-holder is then no package's and is refused 409 when it is all the package has. Resolution does not loosen against main: with one holder there is one candidate, and with two nothing is picked.

  • F1 (a): resolved.
    • A declaration moved to another package or to the environment keeps its connection: connector-connections.ts:240 returns every row.
    • The stranded token is gone: revoke (:650) deletes every answering row, so the moved row is removed through the door.
    • A reconnect restamps the row in place, null included (:488), so it stays one row.
    • Pins: connector-connection-package.test.ts:148 (to another package, to the environment, and the two-holder counterpart) and connector-connection-door.test.ts:1090 (read, dispatch and revoke through the booted plugin after the holder changes).
    • The changeset line that claimed "or the row is bound" is deleted.
  • F1 (b): resolved. An entry stamped P while the name resolves to Q is one holder, so a legacy package-less row answers P's instance. Pinned at connector-connection-door.test.ts:1114 (the commit that makes this head) and at store level at connector-connection-package.test.ts:141. The real composition is still not named, and no longer needs to be: the answer there is main's.
  • N1: resolved. The S3 remedy (changeset line 39) now says the door is refused at every leg under two holders and the row is removed by uninstalling one holder, then revoking.
  • N2: resolved. The dead fallback in revoke is removed; :650 calls answering once and lets the conflict through.
  • N3: moot while one holder stands. A grant handle whose package went stale still reads the row, because every row answers. The per-read evaluation stays carried to 3b, where it matters.

New in the delta, all nits:

  • N4 (nit): the 409 message and changeset line 32 offer bind for a row "naming a package that is not one of the holders". bind refuses any row that already names a package (packages/cli/src/utils/stored-package-references.ts:643, already-bound). The second remedy in the same sentence (uninstall one holder, then revoke) works, so no row is stuck. Message at connector-connections.ts:249-252.
  • N5 (nit): the PR body is stale against this head. Its "What changes / Store" bullet and the 维护者速读 still describe the old rule ("plus a package-less row only where the name's durable resolution binds it to that package"), its S3 line still says "until bound and revoked", and Verification does not list the F1 pins. The seat writes the body; the declaration lines in it are correct.
  • N6 (nit, carry to the narrowing PR): a row's stamp is refreshed only by a reconnect. After a move from A to B the row may still name A while B uses it. Once two holders can boot, if A declares the name again, that row reads as A's and B reads "not connected". Nothing reaches this while the backstop stands. The narrowing PR should restamp or report such rows before it lifts the backstop.
  • N7 (nit): changeset line 7 says "every answer is as before" for a one-holder database, and the list below it keeps one exception. A connect in flight (at most ten minutes) whose recorded package stops declaring the instance is refused 404 at the callback before its code is used. The refusal is right and is listed at line 33; the sentence should except it.

② Semver level

  • '@objectstack/service-automation': patch with Metadata-protocol: none: right, unchanged. No authorable key, value or type moves. The ADR-0087 marker (not-required) still fits.
  • The declaration line Clause-②: yes (widening), in the PR body and at changeset line 7, now holds. The reason the first record refused it is gone: no answer on a supported database tightens. What remains under "What answers differently" needs two holders (which cannot boot), two rows answering one instance (a refused guard), or the in-flight callback of N7. None is a narrowing that owes the (narrowing) arm.
  • Check-runs on 0704ffc0 at the time of reading: 22 registered, 0 failed.
    • Concluded, 3, all skipped: Schedule gate · skip when a green push run covers the commit, Packed-tarball smoke (opt-in), 18.x upgrade harness (opt-in).
    • In progress, 19: Check Changeset, Check PR Size, PR-body guards, Governed Surface Queue Guard, Check Documentation Links, Flag docs affected by code changes, Spec property liveness, the eight Lint · jobs and the four Type Check · jobs.
    • Not yet registered: the CI workflow's jobs, Build Core and the Test Core shards among them, which run the new pins.
  • This verdict is the contract reading of the diff. It certifies no gate on this head: Check Changeset and Test Core have not concluded. The seat's landing check (every PR check green) is still owed, and a red there reopens this head.

③ Boundary flags

  • F1 was escalated in the first record with two options. The dev took "keep the answer", so no maintainer ruling is owed.
  • The fix quotes ADR-0150 red line 3 and epic ruling R2-C as its ground (connector-connections.ts:213-220); both are on the card and say what the code now does.
  • M5, M6, S3: still accepted. M6's sentence was corrected to "while two packages hold the name, the stamped row answers only its own package", which matches the new rule.
  • No os-dev-report for this PR or this round is on the card as read in round 1; the flags read are the PR body's, the changeset's and the commit messages'.
  • File surface: unchanged from round 1. stored-reference-package-backfill.ts (comment only) is still the one path outside claim amendment 6869821095, for the ACCEPT to name. Its comment at :48-50 now understates the rule (it describes only the row left null), which belongs with N5's prose.
  • Author: objectstack-fleet[bot]; no further note is owed.
  • N4 to N7 are nits: accepted deviations, prose-face edits or a carry. They do not need another round from this reviewer.

Implemented-by: claude/project-thread-y6vjs1
Reviewed-by: session_01L9qibzKCovWpJoMZD6U3NT

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#7 · os-tesla · session_01L9qibzuZJBQY4wUQFJbkfx

维护者速读 · PR #33218 @ 0704ffc0 · 2026-11-14T15:00Z

  • 改了什么:连接存储、连接门(读 / 开始 / 回调 / 撤销)、OAuth 回调和派发都带上实例的包。开始连接时把包记进待完成授权,回调只完成那个包的实例(该包不再声明时 404,且在用掉授权码之前拒绝)。存储规则(answering):名字只有一个持有者、或没有注册表可判时,该组织里这个名字的每一行都回答这个实例,与按名字读完全一样;连接时就地认领并改戳,撤销时删掉。只有两个持有者时,每个包才只读自己的行。
  • 为什么改:ADR-0150 D4,objectql: narrow the phase-1 one-holder refusal from environment to namespace for webhook, connector and email_template (ADR-0150 child B, webhook family) #31699 PR 3a 的第二半(第一半 feat(service-automation, metadata-protocol, driver-mongodb, cli): a connector connection is unique per organization and instance (package, name) #33199 已加宽唯一键)。
  • 权限与边界:一持有者兜底仍在,所以支持的库上没有答复变紧。声明移到另一个包或移到环境,连接照旧可用(复审 F1 后改成这样)。新的拒绝只在两持有者(启动时进不来)或一个实例有两行(被守卫拒绝)时出现。
  • 风险与回滚:正文里的「Store」条、维护者速读和 S3 一句描述的是复审前的旧规则,以本评论和 changeset 为准(见下)。回滚即 revert,没有迁移,不改存储结构。
  • 先看:packages/services/service-automation/src/connector-connections.ts 第 213-254 行(answering),以及 src/plugin.ts 的 connectorInstancePackage、connectionDoorInstance。
  • 需要你做的:无,席位落地。
  • 席位意见:落地。Clause-② yes (widening),复审先 FAIL(F1:一持有者的答复会因声明换包而移动),修复后 delta PASS;CI 无失败,非受管面,1019 行。

席位核对

  • 形态: draft → main。正文写 Part of #31699,合并后卡保持打开(3b 和收窄 PR 还在后面);正文其他地方没有挨着别的 open 卡号的闭合关键词。
    • 申报 Clause-②: yes (widening)、Metadata-protocol: none,changeset '@objectstack/service-automation': patch。
    • 文件面 = 接管认领 6857670747,加修订 6869821095(3a-ii);本席恢复认领 6878990173。
  • 前提实测: dev 报告 6878937714(0704ffc0 之前的 6a982f7 上):connector-connection-package.test.ts 13 条、connector-connection-door.test.ts 50 条,经真 ObjectQL / SQLite 与启动的插件。消融:一持有者规则退回「自己的行加未绑定行」红 4 条;退回复审前规则红 F1(b) 钉;去掉 null 改戳红 1 条;另有红线轮的 11 条。@objectstack/service-automation 全量 281 文件、3875 条通过。
  • 门禁 check-runs @ 0704ffc0: 38 success、15 skipped、0 失败、0 进行中(Build Core、Test Core 8 片与 Type Check 四项都已通过,跑过新的 F1 钉)。
  • 契约复审: TIER_DEFAULT(申报面,加宽)。卡线程的隔离审读子代理按简报 6878905826 出记录,本席核对了简报(只给卡、PR 与 check-runs,明确排除派发令与派发方结论)、范围与独立性后采纳。
  • 接受的偏差:
    • stored-reference-package-backfill.ts 只改三行注释(:48-50),不在修订 6869821095 的文件面里;它现在只描述留空的那一行,比新规则说得少,属于文字面。
    • N4:409 文案与 changeset 第 32 行给的 bind 补救对已带包的行不适用(bind 答 already-bound);同一句的第二个补救(卸一个持有者再撤销)可用,没有行卡死。
    • N5:PR 正文过期。以下三处以本评论为准:「Store」条与维护者速读第三条——一持有者或无注册表时,名字的每一行都回答实例(不只是「持久解析指向该包的无包行」);S3——两持有者下门的每一步都被拒,残留行通过卸掉一个持有者再撤销来删(不是「绑定后撤销」);Verification 未列 F1 钉,见 delta 记录与 dev 报告。正文的两条申报行是对的。
    • N7:changeset 第 7 行「every answer is as before」漏了一个例外——进行中的连接(最多十分钟)若其记录的包不再声明实例,回调 404;该拒绝本身正确,第 33 行已列出。
  • 范围外去向: N6(行的戳只在重连时刷新;两持有者可启动之前要重戳或报告)与 N3(授权句柄按读取时求包)带到 3b 与收窄 PR;3b 在本 PR 合并后推送(修订 6870521528,按交接 6871869590 收窄);ConnectorDescriptor.packageId、M5 的带地址连接门、?package=、核心类型集和撤掉一持有者兜底留给收窄 PR。
  • 落地条款: 维护者常设落地令 「按 pm-dispatch 审过的pr你可以自主落地」 (2026-10-30T14:20Z);本卡按「接手 31699」 (2026-11-13T15:34Z) 由本席接手,并按 「车道座位应该使用pm-dispatch 技能审核和落地pr」 (2026-11-14T14:32Z) 落地。
    • 非受管面:check-governed-merges --pr 33218 判定 0/11 命中。1019 行(+947 / -72)≤ 3000。
    • K = 0(PRBASE e540baf8,取自 Plan Test Core shards 的 Affected-set diff base;其后 main 没有新提交)。git merge-tree 无冲突。
    • 没有新的语种包。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xl tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants